/**
 * 一个作为 RESTful 服务的免费 JSON 存储的简化请求工具。
 * 使用要求，需要到下面的link1里面申请API密钥，具体方法可以到链接里面查看。
 * @author weijun.wu
 * @param username {string} 用户名
 * @param Authorization {string} apiId
 * @example 
 *   const jsonbin = new JsonBin('zzwuweijun', 'cadcbc89-6ddd-4892-a490-cbd9f253a750')
 *   jsonbin.get().then(s => console.log(`s`, s))
 * @link link1：https://521github.com/remy/jsonbin
 * @link link2：https://jsonbin.io/create-account
 * @link 其他简化库 https://gitee.com/wavef/jsonbin/
 **/
class JsonBin {
  constructor(username, Authorization) {
    this.base = 'https://jsonbin.org/' + username;
    this.username = username;
    this.Authorization = Authorization;
    if (!username || !Authorization) throw new Error('异常，必须有username和Authorization')
  }
  Authorization = null;
  username = null;
  base = null;

  /**
   * 内部方法，不使用。作用：判断和组成正确的路径。
   **/
  path(path) {
    if (path == null || path == undefined) return '';
    else if (Array.isArray(path)) path = '/' + path.join('/');
    else if (typeof path == 'string' || typeof path == 'number') path = String(path);
    else throw new Error(`异常，请输入正确的路径，如‘/country/province’或是‘['country','province']’`);
    path = path.replaceAll('//', '/');
    if (path.substring(0, 1) != '/') path = '/' + path;
    return path;
  }
  /**
   * 判断是否为json类型
   **/
  isJson(data) { try { return JSON.parse(data); } catch (error) { return data; } }

  // 简单判断是否需要添加Content-Type请求头
  addContentType(data) {
    let headers = { "Content-Type": "application/json" }
    if (typeof data === 'string') {
      data = data.trim();
      if (data.length < 2) return { data, headers: {} };
      let len = data.length;
      if (data[0] === '{' && data[data.length - 1] === '}') return { data, headers };
      else if (data[0] === '[' && data[data.length - 1] === ']') return { data, headers };
      else {
        try {
          JSON.parse(data)
          return { data, headers };
        } catch (error) {
          return { data, headers: {} };
        }
      }
    } else {
      if (typeof data === 'boolean') return { data, headers };
      else if (typeof data === 'number') return { data, headers };
      else if (data === null) return { data, headers: {} };
      else if (data === undefined) return { data, headers: {} };
      else {
        try {
          data = JSON.stringify(data)
          return { data, headers };
        } catch (error) {
          return { data, headers: {} };
        }
      }
    }
  }

  request(url, option, resolve, reject) {
    let status;
    fetch(url, option)
      .then(res => { status = res.status; return res.text() })
      .then(res => { resolve({ status, data: this.isJson(res) }) })
      .catch(e => {
        reject(e); console.error(`get：获取路径：${url} 发生错误：`, e);
      });
  }

  /**
   * 通过正确的路径，获取到响应的json数据
   * @param path {(string|array|number|null|undefined)} 请求路径
   * @return {promise} 返回值
   * @example jsonbin.get().then(s => console.log(`s`, s))
   * @example jsonbin.get('/age/name').then(s => console.log(`s`, s))
   * @example jsonbin.get(['age', 'name']).then(s => console.log(`s`, s))
   **/
  async get(path) {
    return await new Promise(async (resolve, reject) => {
      this.request(this.base + this.path(path), {
        method: 'GET', headers: { authorization: `token ${this.Authorization}` }
      }, resolve, reject)
    })
  }

  /**
   * 替换与合并原有的数据。
   * 基本数据类型和对象类型：合并替换。
   * 数组类型：在原有数据后面添加，既是push方法。
   * @param path {(string|array|number|null|undefined)} 请求路径
   * @param json {json} 要发送的数据
   * @param headers {object} 请求头
   * @return {promise} 返回值
   * @example jsonbin.patch(['age', 'name'], {lock:123}).then(s => console.log(`s`, s))
   * @example jsonbin.patch('/age/name', {lock:123}).then(s => console.log(`s`, s))
   **/
  async patch(path, json, headers = {}) {
    let tem = this.addContentType(json);
    headers = Object.assign(tem.headers, headers);
    json = tem.data;
    return await new Promise(async (resolve, reject) => {
      this.request(this.base + this.path(path),
        {
          method: 'PATCH',
          headers: Object.assign({
            authorization: `token ${this.Authorization}`,
          }, headers),
          body: json
        },
        resolve, reject)
    })
  }

  /**
   * 删除原有的数据，替换成现在的数据
   * @param path {(string|array|number|null|undefined)} 请求路径
   * @param json {json} 要发送的数据
   * @param headers {object} 请求头
   * @return {promise} 返回值
   * @example jsonbin.post(['age', 'name'], {lock:123}).then(s => console.log(`s`, s))
   * @example jsonbin.post('/age/name', {lock:123}).then(s => console.log(`s`, s))
   **/
  async post(path, json, headers = {}) {
    let tem = this.addContentType(json);
    headers = Object.assign(tem.headers, headers);
    json = tem.data;
    return await new Promise(async (resolve, reject) => {
      this.request(this.base + this.path(path),
        {
          method: 'POST',
          headers: Object.assign({ authorization: `token ${this.Authorization}` }, headers),
          body: json
        },
        resolve, reject)
    })
  }

  /**
   * 删除指定路径的数据
   * @param path {(string|array|number|null|undefined)} 请求路径
   * @return {promise} 返回值
   * @example jsonbin.delete(['age', 'name']).then(s => console.log(`s`, s))
   * @example jsonbin.delete('/age/name').then(s => console.log(`s`, s))
   **/
  async delete(path) {
    return await new Promise(async (resolve, reject) => {
      this.request(this.base + this.path(path),
        {
          method: 'DELETE',
          headers: { authorization: `token ${this.Authorization}` }
        },
        resolve, reject)
    })
  }
}

// 测试
// (() => {
//   const jsonbin = new JsonBin('zzwuweijun', 'cadcbc89-6ddd-4892-a490-cbd9f253a750')
//   jsonbin.get().then(s => console.log(`s`, s))
//   // console.log(``, jsonbin.path())
//   // console.log(``, jsonbin.path('lsdjife/lsdie/lsjdoi'))
//   // console.log(``, jsonbin.path(['lsdjife/lsdie/lsjdoi', 'sdj', 'sldji', '324', 9999, '-ljde']))
// })()

// 测试
// (() => {
//   const jsonbin = new JsonBin('zzwuweijun', 'cadcbc89-6ddd-4892-a490-cbd9f253a750')
//   jsonbin.get().then(s => console.log(`s`, s))
//   // console.log(``, jsonbin.get().then(s => console.log(`s`, s)))
//   // console.log(``, jsonbin.get('lsdjife/lsdie/lsjdoi').then(s => console.log(`s`, s)))
// })()

